package com.denlaku.longan.service.impl;

import com.denlaku.longan.index.IndexComponent;
import com.denlaku.longan.index.IndexHolder;
import com.denlaku.longan.index.IndexQuery;
import com.denlaku.longan.service.IndexComponentService;
import com.denlaku.longan.service.IndexService;
import com.denlaku.longan.service.LexiconItemService;
import com.denlaku.longan.util.Lists;
import com.denlaku.longan.vo.LexiconItem;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.util.BytesRef;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * @author tianx
 */
@Service
@Slf4j
public class IndexServiceImpl implements IndexService {
    @Autowired
    private IndexComponentService indexComponentService;

    @Autowired
    private LexiconItemService lexiconItemService;

    @Override
    public void index(IndexHolder holder) {
        Long tenantId = holder.getTenantId();
        String biz = holder.getBiz();
        List<Document> documents = holder.getDocuments();
        Objects.requireNonNull(tenantId);
        Objects.requireNonNull(biz);
        Objects.requireNonNull(documents);

        try {
            List<LexiconItem> lexiconItems = lexiconItemService.listByTenantId(tenantId);
            IndexComponent indexComponent = indexComponentService.getOrInitIndexComponent(String.valueOf(tenantId), biz,
                    lexiconItems);
            IndexWriter indexWriter = indexComponent.getIndexWriter();

            for (Document document : documents) {
                indexWriter.addDocument(document);
            }
            indexWriter.flush();
            indexWriter.commit();
        } catch (IOException e) {
            log.error("add document error", e);
        }
    }

    @Override
    public void delete(IndexQuery indexQuery) {
        Long tenantId = indexQuery.getTenantId();
        String biz = indexQuery.getBiz();
        String keyword = indexQuery.getKeyword();
        Objects.requireNonNull(tenantId);
        Objects.requireNonNull(biz);
        Objects.requireNonNull(keyword);

        IndexComponent indexComponent = indexComponentService.getIndexComponent(String.valueOf(tenantId), biz);
        if (indexComponent == null) {
            return;
        }
        try {
            IndexWriter indexWriter = indexComponent.getIndexWriter();
            for (String field : ListUtils.emptyIfNull(indexQuery.getFields())) {
                TermQuery query = new TermQuery(new Term(field, new BytesRef(keyword.getBytes())));
                indexWriter.deleteDocuments(query);
            }
            indexWriter.commit();
        } catch (Exception e) {
            log.error("delete index error", e);
        }
    }

    @Override
    public List<Document> search(IndexQuery indexQuery) {
        Long tenantId = indexQuery.getTenantId();
        String biz = indexQuery.getBiz();
        Objects.requireNonNull(tenantId);
        Objects.requireNonNull(biz);
        Objects.requireNonNull(indexQuery.getKeyword());

        List<String> fields = indexQuery.getFields();
        if (CollectionUtils.isEmpty(fields)) {
            return Collections.emptyList();
        }
        try {
            List<LexiconItem> lexiconItems = lexiconItemService.listByTenantId(tenantId);
            IndexComponent indexComponent = indexComponentService.getOrInitIndexComponent(String.valueOf(tenantId), biz,
                    lexiconItems);
            IndexSearcher indexSearcher = indexComponent.getIndexSearcher();

            Analyzer analyzer = indexComponent.getAnalyzer();
            MultiFieldQueryParser parser = new MultiFieldQueryParser(fields.toArray(new String[] {}), analyzer);
            Query query = parser.parse(indexQuery.getKeyword());
            int top = Math.max(indexQuery.getTop(), 1);
            TopDocs search = indexSearcher.search(query, top);

            ScoreDoc[] scoreDocs = search.scoreDocs;
            List<Document> documents = Lists.ofSize(scoreDocs.length);
            for (ScoreDoc scoreDoc : scoreDocs) {
                Document document = indexSearcher.doc(scoreDoc.doc);
                documents.add(document);
            }
            return documents;
        } catch (Exception e) {
            log.error("search error", e);
        }
        return Lists.empty();
    }
}
